package com.soulkun.smarthome.logic.convert

import com.google.gson.Gson
import com.google.gson.TypeAdapter
import com.google.gson.reflect.TypeToken
import com.soulkun.smarthome.logic.enum.ErrorCodeEnum
import com.soulkun.smarthome.logic.enum.StatusCodeEnum
import com.soulkun.smarthome.logic.exception.ApiException
import com.soulkun.smarthome.logic.model.ErrorModel
import com.soulkun.smarthome.logic.model.ResponseModel
import okhttp3.MediaType
import okhttp3.RequestBody
import okhttp3.ResponseBody
import okio.Buffer
import retrofit2.Converter
import retrofit2.Retrofit
import soulkun.library.utils.logE
import java.io.OutputStreamWriter
import java.io.Writer
import java.lang.reflect.Type
import java.nio.charset.Charset

class SoulkunGsonConverterFactory(private val gson: Gson) : Converter.Factory() {

    companion object {
        fun create(): SoulkunGsonConverterFactory {
            return create(Gson())
        }

        fun create(gson: Gson): SoulkunGsonConverterFactory {
            return SoulkunGsonConverterFactory(gson)
        }

        private val MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8")
        private val UTF_8 = Charset.forName("UTF-8")

    }

    override fun responseBodyConverter(
        type: Type,
        annotations: Array<out Annotation>,
        retrofit: Retrofit
    ): Converter<ResponseBody, *> {
        val adapter = gson.getAdapter(TypeToken.get(type))
        return SoulkunGsonResponseBodyConvert(gson, adapter, type)
    }

    override fun requestBodyConverter(
        type: Type,
        parameterAnnotations: Array<out Annotation>,
        methodAnnotations: Array<out Annotation>,
        retrofit: Retrofit
    ): Converter<*, RequestBody> {
        val adapter = gson.getAdapter(TypeToken.get(type))
        return SoulkunGsonRequestBodyConverter(gson, adapter)
    }

    inner class SoulkunGsonRequestBodyConverter<T>(
        private val gson: Gson,
        private val adapter: TypeAdapter<T>
    ) : Converter<T, RequestBody> {

        override fun convert(value: T): RequestBody? {
            val buffer = Buffer()
            val writer: Writer =
                OutputStreamWriter(buffer.outputStream(), UTF_8)
            val jsonWriter = gson.newJsonWriter(writer)
            adapter.write(jsonWriter, value)
            jsonWriter.close()
            return RequestBody.create(MEDIA_TYPE, buffer.readByteString().apply { logE() })
        }

    }

    inner class SoulkunGsonResponseBodyConvert<T>(
        private val gson: Gson,
        private val adapter: TypeAdapter<T>,
        private val type: Type
    ) : Converter<ResponseBody, T> {

        override fun convert(value: ResponseBody): T? {
            try {
                val response = value.string()
                response.logE()
                val result = gson.fromJson(response, ResponseModel::class.java)
                when (result.statusCode) {
                    StatusCodeEnum.Success.code -> {
                        gson.fromJson<T>(
                            response,
                            type
                        ).logE()
                        return gson.fromJson<T>(
                            response,
                            type
                        )
                    }
                    StatusCodeEnum.Failure.code -> {
                        val result = gson.fromJson<ResponseModel<ErrorModel>>(
                            response,
                            object : TypeToken<ResponseModel<ErrorModel>>() {}.type
                        )
                        when (result.data.errorCode) {
                            ErrorCodeEnum.AuthenticationError.code, ErrorCodeEnum.AccessDeniedError.code, ErrorCodeEnum.OneNetError.code, ErrorCodeEnum.DatabaseError.code, ErrorCodeEnum.ParamsError.code -> {
                                throw ApiException(result.data.errorCode, result.data.errorMessage)
                            }
                            else -> {
                                throw ApiException(result.data.errorCode, "服务器内部异常，请联系开发人员")
                            }
                        }
                    }
                    else -> {
                        throw ApiException(ErrorCodeEnum.UnknownError.code, "发生未知异常，请联系开发人员")
                    }
                }
            }finally {
                value.close()
            }
        }

    }


}